package com.googlecode.lightest.core

abstract class QueuedDispatcherAssignmentStrategy
    implements IDispatcherAssignmentStrategy
{
    public static final DEFAULT_POLL_INTERVAL = 100

    def assignmentQueue
    def assignmentTimeout
    def assignmentPollInterval

    QueuedDispatcherAssignmentStrategy() {
        assignmentQueue = Collections.synchronizedList([])
        assignmentTimeout = 0
        assignmentPollInterval = DEFAULT_POLL_INTERVAL
    }

    /**
     * Sets the maximum time a testcase will wait to have a task dispatcher
     * assigned to it, before timing out.
     *
     * @param timeout  allotted time, in milliseconds. Non-positive values will
     * 				   be considered infinite time.
     */
    void setAssignmentTimeout(timeout) {
        assignmentTimeout = timeout
    }

    /**
     * Set the amount of time to wait before retrying a dispatcher assignment.
     * The default will be DEFAULT_POLL_INTERVAL.
     *
     * @param interval  the interval, in milliseconds
     */
    void setAssignmentPollInterval(interval) {
        assignmentPollInterval = interval
    }

    /**
     * Assigns a dispatcher to a testcase, and the testcase to the dispatcher.
     * Returns true if the assignment was successful, and false otherwise.
     * Testcases that cannot be immediately assigned are queued up for
     * assignment in FIFO order, and stay in the queue until assigned, or the
     * timeout elapses (if any).
     *
     * @param testcase
     */
    boolean assign(LightestTestCase testcase) {
         def timeoutTime = (assignmentTimeout < 1
             ? Long.MAX_VALUE
             : System.currentTimeMillis() + assignmentTimeout)

         assignmentQueue << testcase
             
         while (System.currentTimeMillis() < timeoutTime) {
             synchronized (this) {
                 if (assignmentQueue[0] == testcase) {
                     // up to bat!
                     
                     def dispatchers = getAvailableDispatchers(testcase)
    
                     if (dispatchers.size() > 0) {
                         def dispatcher = dispatchers[0]
    
                         dispatcher.setTestCase(testcase)
                         testcase.setDispatcher(dispatcher)
    
                         assignmentQueue.remove(0)
                         return true
                     }
                 }
             }
             
             sleep assignmentPollInterval
         }

         return false
    }

    /**
     * Returns a potentially empty list of currently available (unassigned)
     * task dispatchers for a given testcase. This accommodates the possibility
     * of filtering dispatchers based on testcase attributes, by starting with
     * the result of getCompatibleDispatchers() passing in the testcase.
     *
     * @param testcase  the testcase for which to return the dispatchers
     */
    protected List<ITaskDispatcher>
        getAvailableDispatchers(LightestTestCase testcase)
    {
         def dispatchers = []

         getCompatibleDispatchers(testcase).each {
             if (it.getTestCase() == null) {
                 dispatchers << it
             }
         }

         return dispatchers
    }

    protected abstract List<ITaskDispatcher>
        getCompatibleDispatchers(LightestTestCase testcase)

    void unassign(LightestTestCase testcase) {
        def dispatcher = testcase.getDispatcher()

        dispatcher.setTestCase(null)
        testcase.setDispatcher(null)
    }
}